package com.chenl.provider.DEMOS.Spring.API使用;

import com.alibaba.fastjson.JSONObject;
import com.chenl.provider.DEMOS.currLimit限流.RateLimit;
import com.chenl.provider.DEMOS.缓存预热.AbstractCache;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;

/**
 * 框架等底层相关API/反射相关的使用
 */
@Slf4j
public class SpringApiDemos {
    @Autowired
    private ApplicationContext context;

    public String Demo() throws NoSuchMethodException {
        //1.SpringMvc中拿到接口请求的上下文数据(HttpServletRequest / Response 等),只要是SpringMvc接口请求都可以直接使用---------------------------
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        //2.ApplicationContext的使用------------------------------------------------------------------------------------------------------
            //1 获取spring容器中包含该注解的对象集合 .getBeansWithAnnotation(*.clazz)
        Map<String, Object> beansWithAnnotationMap = this.context.getBeansWithAnnotation(RateLimit.class);//传入注解类名.class获取到所有拥有特定注解的Beans集合(Key为Bean的名称,value为Bean的实例)
        Iterator var5 = beansWithAnnotationMap.entrySet().iterator();
        while (var5.hasNext()) {//遍历
            final Map.Entry<String, Object> entry = (Map.Entry) var5.next();
            final Class<?> classIns = entry.getValue().getClass();
            RateLimit rocketMqListenerAnnotaion = (RateLimit) classIns.getDeclaredAnnotation(RateLimit.class); //获取到当前类上的注解对象
            //获取到注解中的属性
            String key = rocketMqListenerAnnotaion.key();
            String type = rocketMqListenerAnnotaion.type();
            Long bucketMaxTokens = rocketMqListenerAnnotaion.bucketMaxTokens();

            //2 获取某个接口及所有子类 .getBeansOfType(*.clazz) 可以用作缓存预热等功能
            Map<String, AbstractCache> beansOfType = this.context.getBeansOfType(AbstractCache.class);
            if (ObjectUtils.isNotEmpty(beansOfType)) {
                for (Map.Entry<String, AbstractCache> cacheEntry : beansOfType.entrySet()) {
                    AbstractCache bean = cacheEntry.getValue();
                    bean.initCache();
                }
            }

        }
        JoinPoint joinPoint=new MethodInvocationProceedingJoinPoint(null);
        //3.AOP中使用------------------------------------------------------------------------------------------------------------------
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Class<?> targetClass = method.getDeclaringClass();//获取类相关属性
        RateLimit rateLimit = method.getAnnotation(RateLimit.class);//获取当前类上的注解
        this.log.info("==================请求开始==================");
        this.log.info("[请求IP]:{}", request.getRemoteAddr());
        this.log.info("[请求URL]:{}", request.getRequestURL());
        this.log.info("[请求方式]:{}", request.getMethod());
        this.log.info("[请求类名]:{},[请求方法名]:{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
        this.log.info("[请求参数]:{}", JSONObject.toJSONString(this.getParameter(method, joinPoint.getArgs())));
            //3.1  获取方法所实现接口上的方法上的注解,
        Class<?> methodDeclaringClass = method.getDeclaringClass();
        Class<?>[] interfaces = methodDeclaringClass.getInterfaces();
        Method interfaceMethod = interfaces[0].getMethod(method.getName(), method.getParameterTypes());
        ApiOperation[] apiOperations = interfaceMethod.getAnnotationsByType(ApiOperation.class);
        ApiOperation apiOperation = apiOperations[0];
            //3.2  获取接口方法的[参数]上的注解
        List<Parameter> parameters = Arrays.asList(method.getParameters());
        FLAG:for (Parameter parameter : parameters) {
            Annotation[] annotations = parameter.getAnnotations();
            for (Annotation annotation : annotations) {
                if (annotation instanceof PathVariable) {
                    String requestURI = request.getRequestURI();
                    PathVariable pathVariable = (PathVariable) annotation;
                    String s = pathVariable.value();//获取方法参数上PathVariable注解中,value属性的值
                    break FLAG;
                }
            }
        }
            //3.3  获取接口方法上的注解
        List<Annotation> annotations = Arrays.asList(method.getAnnotations());
        //3.4 获取到请求收到的所有请求头并打印
        Enumeration<String> er = request.getHeaderNames();
        while(er.hasMoreElements()){
            String name	=(String) er.nextElement();
            String value = request.getHeader(name);
            log.info(name+"="+value);
        }
        return "ok";
    }
    //注解的使用
    //@DependsOn(value = {"",""}) //注解的作用是确保当前bean在value属性所指定的bean初始化之后才会被初始化。

    private Object getParameter(Method method, Object[] args) {
        List<Object> argList = new ArrayList();
        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < parameters.length; ++i) {
            RequestBody requestBody = (RequestBody) parameters[i].getAnnotation(RequestBody.class);
            if (requestBody != null) {
                argList.add(args[i]);
            }
            RequestParam requestParam = (RequestParam) parameters[i].getAnnotation(RequestParam.class);
            if (requestParam != null) {
                Map<String, Object> map = new HashMap();
                String key = parameters[i].getName();
                if (!StringUtils.isEmpty(requestParam.value())) {
                    key = requestParam.value();
                }
                map.put(key, args[i]);
                argList.add(map);
            }
        }
        if (argList.size() == 0) {
            return null;
        } else if (argList.size() == 1) {
            return argList.get(0);
        } else {
            return argList;
        }
    }
}
